<?php
require_once 'includes/tags.php';

class Model_Search extends RL_Model2 {
	private $sstring = '';
	private $words = array();
	private $lpage = 1;
	private $pages = 0;
	private $tags = array();

	private $max_words = 5;
	private $per_page = 5;

	private $posters = array();
	private $sections = array();
	private $communities = array();
	private $foundedTags = array();

	private $searchInComments = false;

	public function setSearchString( $sstring = '' ) {
		if ( !empty($sstring) ) {
			mb_language('ru');
			mb_internal_encoding("UTF-8");
			mb_detect_order(array('UTF-8','WINDOWS-1251'));
			mb_regex_encoding('UTF-8');
			setlocale(LC_CTYPE, array("ru_RU.UTF-8","ru_RU","russian","ru_SU","ru"));
			
			$sstring = mb_strtolower($sstring);
		
			$sstring = mb_ereg_replace( '^\s+', '', $sstring );
			$sstring = mb_ereg_replace( '\s+$', '', $sstring );
			$sstring = mb_ereg_replace( '\s+', ' ', $sstring );

			//$sstring_enc = mb_convert_encoding( $sstring, 'windows-1251', 'utf-8' );
			//
			//setlocale(LC_CTYPE, array("ru_RU.cp1251","ru_RU","russian","ru_SU","ru"));
			//
			//$sstring_enc = preg_replace( '/^\s+/', '', $sstring_enc );
			//$sstring_enc = preg_replace( '/\s+$/', '', $sstring_enc );
			//$sstring_enc = preg_replace( '/\s+/', ' ', $sstring_enc );
			//
			//$sstring = mb_convert_encoding( $sstring_enc, 'utf-8', 'windows-1251' );
			//
			//setlocale(LC_CTYPE, array("ru_RU.UTF-8","ru_RU","russian","ru_SU","ru"));

			$this->sstring = $sstring;

			$words = explode( ' ', $sstring );
			$words = array_flip($words);
			$words = array_flip($words);

			$words = array_chunk( $words, $this->max_words );
			$words = $words[0];

			$se_words = array();
			foreach ( $words as $w ) {
				$w_sql = $this->store->db->quote($w);

				$result = $this->store->query('select group_id from se_pre_words ' .
				" where word = {$w_sql}");
				if ( $result instanceof MysqlDBResult ) {
					$rec = $result->next();
					if ( !empty($rec) ) {
						$group_id = $rec['group_id'];

						$result = $this->store->query( 'select word from se_pre_words ' .
						" where group_id = {$group_id}" );
						if ( $result instanceof MysqlDBResult ) {
							while ( $rec = $result->next() ) {
								$se_words[$rec['word']] = true;
							}
						}
					} else {
						$se_words[$w] = true;
					}
				} else {
					$se_words[$w] = true;
				}
			}

			$this->words = $se_words;
		}
	}

	public function setLPage( $lpage = 0 ) {
		$this->lpage = abs($lpage+0);
		if ( $this->lpage <= 0 ) {
			$this->lpage = 1;
		}
	}

	public function setTag( $tag ) {
		$this->tags[htmlspecialchars(mb_strtolower($tag))] = true;
	}

	public function setSearchInComments( $flag ) {
		$this->searchInComments = $flag;
	}

	public function getLPage() {
		return $this->lpage;
	}

	public function getPages() {
		return $this->pages;
	}

	public function getTags() {
		if ( !empty($this->tags) ) {
			$tags = array();
			foreach ( array_keys($this->tags) as $tag ) {
				$tags[]['title'] = $tag;
			}

			return $tags;
		}

		return $this->tags;
	}

	public function getFoundedTags() {
		return $this->foundedTags;
	}

	public function getWords() {
		return $this->words;
	}

	public function getItems() {
		$allFounded = $this->getOIDsFounds();
		
		$this->foundedTags = $this->getTagsInFounded($allFounded);

		if ( !empty($this->tags) ) {
			$allItems = $this->filterFoundedByTag($allFounded);
		} else {
			$allItems = $allFounded;
		}

		$this->pages = (int) (count($allItems) / $this->per_page);
		if ( count($allItems) % $this->per_page > 0 ) {
			$this->pages++;
		}
		if ( $this->lpage > $this->pages && $this->pages > 0 ) {
			$this->lpage = $this->pages;
		}
		$from = ($this->lpage - 1) * $this->per_page;
		$founded = array_slice($allItems, $from, $this->per_page);

		$vm = $this->mm->getModel("Vote", "m");
		$aa = $this->mm->aa;

		$this->mm->rh->useClass("AggregatedModel_ItemProcessor_Interface");
		$this->mm->rh->useClass("AggregatedModel_ItemProcessor_Common");

		$items = array();
		foreach ( $founded as $found ) {
			$data = array();
			
			$item = $this->store->getObject($found);
			
			if ( empty($item) ) {
				continue;
			}

			if ( 'fcomment' == $item->type ) {
				$comment_id = $item->id;
				$item = $this->store->getObject($item->data['item_oid']);
				$item->data['go2comment'] = $comment_id;
			}

			$data['post'] = $item->data;

			$atags = array();
			$count = 0;
			foreach ( tags_split($item->data['tags']) as $i=>$tag ) {
				$atags[$count]['title'] = $tag;
				if ( isset($this->tags[htmlspecialchars(mb_strtolower($tag))]) ) {
					$atags[$count]['is_current'] = true;
				}
				$count++;
			}
			$data['tags'] = $atags;

			$data['post']['oid'] = $item->id;
			$data['post']['type'] = $item->type;
			$data['post']['ctime'] = new RL_CyrDate($item->ctime);
			$data['post']['poster'] = $this->getPoster($data['post']['posted_by_id']);

			$data['acl'] = $aa->getAcl( $data['post']['oid'], $aa->acl_data['user_id'],
			$data['post']['community_oid'] );

			if ( isset($data['post']['tdi_oid']) && !empty($data['post']['tdi_oid']) ) {
				$data['section'] = $this->getSection($data['post']['tdi_oid']);
			}

			if ( isset($data['post']['community_oid']) && !empty($data['post']['community_oid']) ) {
				$community = $this->getCommunity($data['post']['community_oid']);

				$data['post']['community_url'] = $community['community_url'];
				$data['post']['community_title'] = $community['title'];
				$data['community']['url'] = $community['community_url'];
				$data['community']['title'] = $community['title'];
			}

			if ( 'fvote' == $data['post']['type'] ) {
				if ( isset($data['post']['answers']) && !empty($data['post']['answers'])
				&& isset($data['post']['votes']) && !empty($data['post']['votes']) ) {

					$data['post']['overall_voters'] = 0;
					foreach ($data['post']['votes'] as $i=>$vote) {
						$data['post']['overall_voters'] += $vote;
						$data['post']['answers'][$i]['voters'] = $vote;
					}
				}

				$data['already_voted'] = $vm->checkUserVote($data['post']['oid'], $aa->acl_data['user_id']);
				$data['can_vote'] = (isset($aa->acl_data['user_id'])) ? true : false;
			} else if ( 'fimage' == $data['post']['type'] ) {
				$cname = "AggregatedModel_ItemProcessor_ImageType";
				$this->mm->rh->useClass($cname);
				$ig = new $cname($this->mm->rh);
				$image_data = $ig->processItem( $item->data );
				$data['image'] = $image_data['image'];
			} else if ( 'ffile' == $data['post']['type'] ) {
				$cname = "AggregatedModel_ItemProcessor_FileType";
				$this->mm->rh->useClass($cname);
				$ig = new $cname($this->mm->rh);
				$file_data = $ig->processItem( $item->data );
				$data['file'] = $file_data['file'];
			} else if ( 'freview' == $data['post']['type'] ) {
				$data['post']['subject'] = $data['post']['subject_title'];
				$data['post']['authors'] = $data['post']['subject_authors'];
				$data['post']['url'] = $data['post']['subject_url'];
				$data['post']['description'] = $data['post']['subject_description'];
			}

			$fc_model = $this->mm->getModel("FlatComments", "m");
			$data['post']['commentscount'] = $fc_model->commentsCount($data['post']['oid']);

			$items[] = $data;
		}

		return $items;
	}

	private function getOIDsFounds() {
		$user_oids = $this->getUserOIDs();

		$community_oid = 0;
		if ( isset($this->mm->aa->acl_data['comity_oid']) && !empty($this->mm->aa->acl_data['comity_oid']) ) {
			$community_oid = $this->mm->aa->acl_data['comity_oid'];
		}
		$tdi_oid = 0;
		if ( isset($this->mm->aa->acl_data['tdi_oid']) && !empty($this->mm->aa->acl_data['tdi_oid']) ) {
			$tdi_oid = $this->mm->aa->acl_data['tdi_oid'];
		}

		if ( empty($user_oids) ) {
			return array();
		}
		
		if ( $this->searchInComments ) {
			$sql = 'select (select count(*) FROM t_acl_tree as node ' .
      ' left join t_acl_tree as parent on parent.lft <= node.lft and parent.rgt >= node.rgt ' .
      ' left outer join t_acl_list on parent.id_object = t_acl_list.id_acl_tree ' .
      ' where node.id_object = x.oid ' .
      ' and t_acl_list.id_subject in ( ' . join(',', $user_oids) . ' ) ' .
      ' and t_acl_list.`action` in ("item_read", "comment_read") ' .
      ' and t_acl_list.opt = 2) as disabled, ' .
			' x.oid, sum(x.rel) as rel, o.type ' .
			' from se_x x ' .
			' left join se_indexed i on i.oid = x.oid ' .
			' left join se_words w on w.id = x.word_id ' .
			" left join {$this->store->db->db_prefix}objects o on x.oid = o.oid " .
			" left join {$this->store->db->db_prefix}class_comment c ON c.oid = x.oid ";
			
			if ( $community_oid > 0 || $tdi_oid > 0 ) { //|| !empty($this->tags)
				$sql .= " left join {$this->store->db->db_prefix}class_allfeed af on af.oid = x.oid ";
			}

			$sql .= " left join {$this->store->db->db_prefix}class_allfeed items on items.oid = c.item_oid ";
			$sql .= ' where ';

			if ( $community_oid > 0 || $tdi_oid > 0 ) {
				if ( $tdi_oid > 0 ) {
					$sql .= " (af.tdi_oid = {$tdi_oid} or " .
					" (o.type = 'fcomment' and items.community_oid = {$community_oid} and " .
					" items.tdi_oid = {$tdi_oid})) and ";
				} else if ( $community_oid > 0 ) {
					$sql .= " (af.community_oid = {$community_oid} or " .
					" (o.type = 'fcomment' and items.community_oid = {$community_oid})) and ";
				}
			}
		} else {
			$sql = 'select (select count(*) FROM t_acl_tree as node ' .
      ' left join t_acl_tree as parent on parent.lft <= node.lft and parent.rgt >= node.rgt ' .
      ' left outer join t_acl_list on parent.id_object = t_acl_list.id_acl_tree ' .
      ' where node.id_object = x.oid and o.type != "fcomment" ' .
      ' and t_acl_list.id_subject in ( ' . join(',', $user_oids) . ' ) ' .
      ' and t_acl_list.`action` in ("item_read") ' .
      ' and t_acl_list.opt = 2) as disabled, ' .
			' x.oid, sum(x.rel) as rel, o.type ' .
			' from se_x x ' .
			' left join se_indexed i on i.oid = x.oid ' .
			' left join se_words w on w.id = x.word_id ' .
			" left join {$this->store->db->db_prefix}objects o on x.oid = o.oid ";

			if ( $community_oid > 0 || $tdi_oid > 0 ) { // || !empty($this->tags)
				$sql .= " left join {$this->store->db->db_prefix}class_allfeed af on af.oid = x.oid ";
			}

			$sql .= ' where ';

			if ( $community_oid > 0 || $tdi_oid > 0 ) {
				if ( $tdi_oid > 0 ) {
					$sql .= " af.tdi_oid = {$tdi_oid} and ";
				} else if ( $community_oid > 0 ) {
					$sql .= " af.community_oid = {$community_oid} and ";
				}
			}
		}

		if ( isset($this->mm->rh->REQUEST['search_options']) &&
		'title_search' == $this->mm->rh->REQUEST['search_options'] ) {
			$sql .= ' x.title > 0 and ';
		}

		$inline = $this->getInlineWords();
		$sql .= '(' . join( ' or ', $inline ) . ')';
		$sql .= ' group by x.oid having `disabled` = 0 order by rel desc, x.oid desc';

		//print $sql;exit;

		$result = $this->store->query($sql);
		if ( !($result instanceof MysqlDBResult) ) {
			return 0;
		}

		$founded = array();
		while ( $rec = $result->next() ) {
			$founded[] = $rec['oid'];
		}
		return $founded;
	}

	private function getInlineWords() {
		$inline = array();
		foreach ( array_keys($this->words) as $word ) {
			$word = $this->store->db->quote($word);
			$inline[] = " w.name = {$word} ";
		}

		return $inline;
	}

	private function getUserOIDs() {
		$user_oids = array();

		if ( isset($this->mm->aa) && !empty($this->mm->aa) ) {
			$aa = $this->mm->aa;
			if ( isset($aa->acl_data['user_id']) && !empty($aa->acl_data['user_id']) ) {
				$user_oids[] = $aa->acl_data['user_id'];
			}
			if ( isset($aa->acl_data['role']['oid']) && !empty($aa->acl_data['role']['oid']) ) {
				$user_oids[] = $aa->acl_data['role']['oid'];
			}
		}

		return $user_oids;
	}

	private function getPoster($poster_id) {
		if ( !isset($this->posters[$poster_id]) || empty($this->posters[$poster_id]) ) {
			$user = $this->store->getObject($poster_id);

			$poster = array( 'status' => $user->data['status'],
			'name' => $user->data['fullname'],
			'avatar' => $user->data['avatar'], 'oid' => $user->id);

			$this->posters[$poster_id] = $poster;
		}

		return $this->posters[$poster_id];
	}

	private function getSection( $section_oid ) {
		if ( !isset($this->sections[$section_oid]) || empty($this->sections[$section_oid]) ) {
			$object = $this->store->getObject($section_oid);

			$section = $object->data;

			$this->sections[$section_oid] = $section;
		}

		return $this->sections[$section_oid];
	}

	private function getCommunity( $community_oid ) {
		if ( !isset($this->communities[$community_oid]) || empty($this->communities[$community_oid]) ) {
			$object = $this->store->getObject($community_oid);

			$community = $object->data;

			$this->communities[$community_oid] = $community;
		}

		return $this->communities[$community_oid];
	}

	private function getTagsInFounded( $allFounded ) {
		$oid_str = join( ',', $allFounded );

		$sql = "select tags from {$this->store->db->db_prefix}class_allfeed " .
		" where oid in ({$oid_str})";

		$result = $this->store->query($sql);
		if ( !($result instanceof MysqlDBResult) ) {
			return array();
		}

		$tags = array();
		$min_count = $max_count = null;
		while ( $rec = $result->next() ) {
			$parsed = tags_split($rec['tags']);

			$count_parsed = count($parsed);
			if (isset($max_count)) {
				if ($count_parsed > $max_count) $max_count = $count_parsed;
				if ($count_parsed < $min_count) $min_count = $count_parsed;
			} else {
				$max_count = $min_count = $count_parsed;
			}

			foreach ( $parsed as $item ) {
				$item_name = mb_strtolower($item);

				if ( isset($tags[$item_name]['name']) ) {
					$tags[$item_name]['count']++;
				} else {
					$tags[$item_name] = array( 'name'=>$item );
					$tags[$item_name]['count'] = 1;
					$tags[$item_name]['url'] = '?tag=' . htmlspecialchars($item) .
					'&sstring=' . htmlspecialchars($this->sstring);
				}
			}
		}

		$weights = 7; /* возможный диапазон весов [1..7] */
		$range = $max_count - $min_count;

		foreach ( $tags as $key=>&$item ) {
			$c = $item['count'];

			$item['weight'] =
			($range) /* eсли есть разброс */
			? round(($c - $min_count) * ($weights - 1) / ($range)) + 1  /* м.б. от 1 до $weights */
			: round($weights / 2) /* иначе -- среднее */
			;

			$item['is_current'] = isset($this->tags[$key]);
		}

		return $tags;
	}

	private function filterFoundedByTag( $allFounded ) {
		$oid_str = join( ',', $allFounded );

		$sql = "select oid from {$this->store->db->db_prefix}class_allfeed " .
		" where oid in ({$oid_str}) and oid in ({$oid_str})";

		foreach ( array_keys($this->tags) as $tag ) {
			$sql .= " and lower(tags) rlike '(,|^){$tag}(,|$)' ";
		}

		$result = $this->store->query($sql);
		if ( !($result instanceof MysqlDBResult) ) {
			return array();
		}

		$with_tags = array();
		while ( $rec = $result->next() ) {
			$with_tags[$rec['oid']] = true;
		}

		$filtered = array();
		foreach ($allFounded as $oid) {
			if ( isset($with_tags[$oid]) && !empty($with_tags[$oid]) ) {
				$filtered[] = $oid;
			}
		}

		return $filtered;
	}
}